﻿using System;
using System.Collections.Generic;
using System.Linq;
using UMC.Data;
using UMC.Net;
using UMC.Web;
using System.Security.Cryptography.X509Certificates;
using UMC.Host;
using UMC.Security;
using System.Runtime.InteropServices;
using NSec.Cryptography;
using System.Net;
using System.Threading.Tasks;
using System.Net.Sockets;

namespace UMC.ITME
{
    public class WebResource : UMC.Data.WebResource
    {
        //         public virtual IPEndPoint Proxy()
        //         {
        // // IPEndPoint t=new IPEndPoint()
        //         }
        public virtual async Task<WebMeta> GetBridgeConfig(StringValue host)
        {

            var cName = await Bridge.HttpBridgeServier.CName(host);

            var webR = Bridge.HttpBridgeServier.Sign(new Uri(UMC.Net.APIProxy.Uri, $"SiteConfig?Domain={host}&CName={cName}").WebRequest());

            try
            {
                using (var mime = await Bridge.HttpBridge.GetAsync(webR))
                {
                    if (UMC.Data.Utility.TryParse(mime.Second.Span, 10, out int value))
                    {
                        if (value == (int)HttpStatusCode.OK)
                        {
                            var siteD = UMC.Data.JSON.Deserialize<UMC.Web.WebMeta>(mime.Memory.Temp());
                            return siteD;
                        }
                    }
                }
            }
            catch
            {

            }
            return null;
        }
        public override void Remember(string root, Identity identity, string password)
        {
            if (String.IsNullOrEmpty(root) == false)
            {
                DataFactory.Instance().Put(new Entities.Cookie
                {
                    Domain = root,
                    user_id = identity.Id.Value,
                    Account = identity.Name,
                    IndexValue = 0
                });
                UMC.Data.DataFactory.Instance().Password(SiteConfig.MD5Key(root, identity.Id.Value.ToString(), "0"), password);
            }
        }
        public override void Push(Uri uri, Guid tid, params object[] objs)
        {
            var str = JSON.Serialize(objs);
            var device = Utility.Guid(tid);

            foreach (var p in Net.NetSubscribe.Subscribes)
            {
                try
                {
                    new Uri($"http://{p.Address}:{p.Port}/UMC.WS/{device}")
                    .WebRequest().Post(str, w =>
                    {

                    });
                }
                catch
                {

                }
            }
            UMC.Host.HttpWebSocket.Send(tid, str);

        }
    }
    public class Utility : UMC.Data.Utility
    {
        public static bool IsInternalIP(IPAddress address)
        {
            return Socks5Server.IsInternalIP(address.GetAddressBytes());
        }
        public static bool IsInternalIP(ReadOnlySpan<byte> address)
        {
            return Socks5Server.IsInternalIP(address);
        }
        [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
        internal unsafe static extern int crypto_sign_ed25519_sk_to_curve25519(byte* curve25519_sk, byte* ed25519_sk);

        [DllImport("libsodium", CallingConvention = CallingConvention.Cdecl)]
        internal unsafe static extern int crypto_sign_ed25519_pk_to_curve25519(byte* curve25519_pk, byte* ed25519_pk);

        public static byte[] ToCurvePublicKey(Span<byte> pkEd25519)
        {
            Span<byte> pcurve25519 = stackalloc byte[32];
            unsafe
            {
                fixed (byte* pk = pkEd25519)
                {
                    fixed (byte* pcur = pcurve25519)
                    {
                        crypto_sign_ed25519_pk_to_curve25519(pcur, pk);
                    }
                }

            }
            return pcurve25519.ToArray();

        }
        public static bool ApiVerify(ReadOnlySpan<byte> data, ReadOnlySpan<byte> signature)
        {
            var pubKey = NSec.Cryptography.PublicKey.Import(NSec.Cryptography.SignatureAlgorithm.Ed25519, Utility.FromBase64String("XiIeBP6LMDgyJZvULYlTjVRtnk2Xw9Dq6XmNHmYo21Y"), KeyBlobFormat.RawPublicKey);
            return SignatureAlgorithm.Ed25519.Verify(pubKey, data, signature);
        }
        public static void ToCurveSecretKey(Span<byte> skEd, Span<byte> skCurve)
        {
            unsafe
            {
                fixed (byte* sked = skEd)
                {
                    fixed (byte* skc = skCurve)
                    {
                        crypto_sign_ed25519_sk_to_curve25519(skc, sked);
                    }
                }

            }

        }
        public static void SaveDNS()
        {
            var dns = new ProviderConfiguration();
            var em = UMC.Net.NetProxy.DNS().GetEnumerator();
            while (em.MoveNext())
            {
                if (em.Current.Value.Item2.HasFlag(NetProxy.DNSType.DNS))
                {
                    dns.Add(Provider.Create(em.Current.Key, em.Current.Value.Item2.HasFlag(NetProxy.DNSType.IP6) ? "dns6" : "dns"));
                }
                else if (em.Current.Value.Item1 == int.MaxValue && em.Current.Value.Item3.Length > 0)
                {
                    dns.Add(Provider.Create(em.Current.Key, em.Current.Value.Item3[0].ToString()));
                }
            }
            UMC.Data.Reflection.Configuration("dns", dns);

        }

        private const int StackallocThreshold = 512;
        internal static byte[] UrlDecode(ReadOnlySpan<byte> bytes)
        {
            int decodedBytesCount = 0;
            int count = bytes.Length;
            Span<byte> decodedBytes = count <= StackallocThreshold ? stackalloc byte[StackallocThreshold] : new byte[count];

            for (int i = 0; i < count; i++)
            {
                byte b = bytes[i];

                if (b == '+')
                {
                    b = (byte)' ';
                }
                else if (b == '%' && i < count - 2)
                {
                    int h1 = FromChar(bytes[i + 1]);
                    int h2 = FromChar(bytes[i + 2]);

                    if ((h1 | h2) != 0xFF)
                    {
                        b = (byte)((h1 << 4) | h2);
                        i += 2;
                    }
                }

                decodedBytes[decodedBytesCount++] = b;
            }

            return decodedBytes.Slice(0, decodedBytesCount).ToArray();
        }
        public static int UserAgent(ReadOnlySpan<byte> bytes)
        {
            var bs = System.Buffers.ArrayPool<byte>.Shared.Rent(bytes.Length + 16);
            int size = 0;
            try
            {
                int start = 0;
                for (var i = 0; i < bytes.Length; i++)
                {
                    if ('0' <= bytes[i] && bytes[i] <= '9')
                    {
                        if (start < i)
                        {
                            int len = i - start;
                            bytes.Slice(start, len).CopyTo(bs.AsSpan(size));
                            size += len;
                        }
                        else
                        {
                            start = i + 1;
                        }
                    }

                }
                if (start < bytes.Length)
                {
                    int len = bytes.Length - start;
                    bytes.Slice(start, len).CopyTo(bs.AsSpan(size));
                    size += len;
                }
                using (var md5 = System.Security.Cryptography.MD5.Create())
                {

                    md5.TryComputeHash(bs.AsSpan(0, size), bs.AsSpan(size), out var l);
                    return Utility.IntParse(bs.AsSpan(size, 16));
                }
            }
            finally
            {
                System.Buffers.ArrayPool<byte>.Shared.Return(bs);
            }
            // var ms=÷
        }
        public static bool IsIP(StringValue stringValue)
        {
            var spsn = stringValue.Span;
            foreach (var s in spsn)
            {
                switch (s)
                {
                    case 65:
                    case 66:
                    case 67:
                    case 68:
                    case 69:
                    case 98:
                    case 99:
                    case 100:
                    case 101:
                    case 102:
                    case 48:
                    case 49:
                    case 50:
                    case 51:
                    case 52:
                    case 53:
                    case 54:
                    case 55:
                    case 56:
                    case 57:
                    case 46:
                        break;
                    default:
                        return false;
                }
            }
            return true;
        }
        public static StringValue LocalFinger()
        {
            return HttpsStream.LocalFinger();
        }
        public static bool IsHost(ReadOnlySpan<byte> host)
        {
            // var spsn = stringValue.Span;
            foreach (var s in host)
            {
                switch (s)
                {
                    case 97:
                    case 98:
                    case 99:
                    case 100:
                    case 101:
                    case 102:
                    case 103:
                    case 104:
                    case 105:
                    case 106:
                    case 107:
                    case 108:
                    case 109:
                    case 110:
                    case 111:
                    case 112:
                    case 113:
                    case 114:
                    case 115:
                    case 116:
                    case 117:
                    case 118:
                    case 119:
                    case 120:
                    case 121:
                    case 122:
                    case 48:
                    case 49:
                    case 50:
                    case 51:
                    case 52:
                    case 53:
                    case 54:
                    case 55:
                    case 56:
                    case 57:
                    case 58:
                    case 45:
                    case 46:
                        break;
                    default:
                        return false;
                }
            }
            return true;

        }
        //     public static int FromChar(int c)
        //     {
        //         return c >= CharToHexLookup.Length ? 0xFF : CharToHexLookup[c];
        //     }
        //     static byte[] CharToHexLookup = new byte[]{
        //         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 15
        //         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 31
        //         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 47
        //         0x0,  0x1,  0x2,  0x3,  0x4,  0x5,  0x6,  0x7,  0x8,  0x9,  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 63
        //         0xFF, 0xA,  0xB,  0xC,  0xD,  0xE,  0xF,  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 79
        //         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 95
        //         0xFF, 0xa,  0xb,  0xc,  0xd,  0xe,  0xf,  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 111
        //         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 127
        //         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 143
        //         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 159
        //         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 175
        //         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 191
        //         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 207
        //         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 223
        //         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 239
        //         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF  // 255
        //    };
        public static String MD5(System.Guid guid)
        {
            using (var md5 = System.Security.Cryptography.MD5.Create())
            {
                return Guid(new System.Guid(md5.ComputeHash(guid.ToByteArray())));
            }
        }
        public static readonly StringValue UmcRequestUserName = new StringValue("Umc-Request-User-Name".UTF8());
        public static readonly StringValue UmcRequestUserAlias = new StringValue("Umc-Request-User-Alias".UTF8());
        public static readonly StringValue UmcRequestUserRoles = new StringValue("Umc-Request-User-Roles".UTF8());
        public static readonly StringValue UmcRequestUserId = new StringValue("Umc-Request-User-Id".UTF8());
        public static readonly StringValue UmcRequestUserOrganizes = new StringValue("Umc-Request-User-Organizes".UTF8());
        public static readonly StringValue UmcRequestUserTime = new StringValue("Umc-Request-User-Time".UTF8());
        public static readonly StringValue UmcRequestUserSign = new StringValue("Umc-Request-User-Sign".UTF8());
        // public static String NameValue(NameValueCollection Headers)
        // {
        //     var sb = new StringBuilder();
        //     for (var i = 0; i < Headers.Count; i++)
        //     {
        //         sb.AppendFormat("{0}: {1}", Headers.GetKey(i), Headers.Get(i));
        //         sb.AppendLine();
        //     }
        //     sb.AppendLine();
        //     return sb.ToString();
        // }

        public static int HashCode(ReadOnlySpan<byte> Segments)
        {
            var hash = new HashCode();
            hash.AddBytes(Segments);
            return hash.ToHashCode();
        }
        static Web.WebMeta FromValue(int index, Span<byte> html, out int tagEndIndex)
        {
            tagEndIndex = -1;
            var startIndex = 0;
            while (index > -1)
            {
                index--;
                switch (html[index])
                {
                    case 32://(byte)' ':
                            // case (byte)' ':
                        break;
                    case 60:
                        // case (byte)'<':
                        startIndex = index;
                        break;

                    case 34: //'"'
                    case 39://(byte)'\'':
                            // case (byte)'\'':
                            // case (byte)'"':
                        index = html.LastIndexOf(html[index], index - 1);
                        break;
                }
                if (startIndex > 0)
                {
                    break;
                }
            }
            var start = startIndex + 1;
            var attrStart = -1;
            var attrName = String.Empty;

            var webMeta = new Web.WebMeta();
            var IsFindTag = true;
            while (IsFindTag)
            {
                start++;
                switch (html[start])
                {
                    case 13://(byte)'\r':
                    case 9:// (byte)'\t':
                    case 10:// (byte)'\n':
                    case 32://(byte)' ':
                            // case (byte)'\r':
                            // case (byte)'\n':
                            // case (byte)'\t':
                            // case (byte)' ':
                        IsFindTag = false;
                        webMeta.Put("tag", html.Slice(startIndex + 1, start - startIndex - 1).UTF8());
                        break;
                    default:
                        IsFindTag = start < html.Length;
                        break;
                }
            }
            var startValue = -1;
            while (start < html.Length)
            {
                switch (html[start])
                {
                    case 13://(byte)'\r':
                    case 9:// (byte)'\t':
                    case 10:// (byte)'\n':
                    case 32://(byte)' ':
                            // case (byte)'\r':
                            // case (byte)'\n':
                            // case (byte)'\t':
                            // case (byte)' ':
                        if (startValue == -1)
                        {
                            if (attrStart < start - 1 && attrStart > 0)
                            {
                                attrName = html.Slice(attrStart, start - attrStart).UTF8();
                                webMeta.Put(attrName.ToLower(), String.Empty);
                            }

                        }
                        else
                        {
                            webMeta.Put(attrName.ToLower(), html.Slice(startValue, start - startValue).UTF8());
                            startValue = -1;
                        }
                        attrStart = start + 1;
                        break;
                    case 61:
                        // case (byte)'=':
                        if (attrStart < start - 1)
                        {
                            attrName = html.Slice(attrStart, start - attrStart).UTF8();
                        }
                        attrStart = start + 1;
                        startValue = attrStart;
                        break;

                    case 34: //'"'
                    case 39://(byte)'\'':
                            // case (byte)'\'':
                            // case (byte)'"':
                            // html.in
                            // Array.IndexOf(html,html[start]);
                        var startValueIndex = html.IndexOf(html[start], start + 1);
                        if (startValueIndex > 0)
                        {
                            webMeta.Put(attrName.ToLower(), System.Web.HttpUtility.HtmlDecode(html.Slice(start + 1, startValueIndex - start - 1).UTF8()));
                        }
                        start = startValueIndex;
                        startValue = -1;
                        attrStart = start + 1;

                        break;
                    case 47:
                        // case (byte)'/':
                        tagEndIndex = start + 1;
                        return webMeta;
                    case 62:
                        // case (byte)'>':
                        tagEndIndex = start;
                        return webMeta;
                    default:
                        break;
                }
                start++;
            }
            return webMeta;
        }
        static String FromValue(Span<byte> html, int index, bool isForm, out int endIndex)
        {
            var ms = FromValue(index, html, out endIndex);
            if (ms.ContainsKey("disabled"))
            {
                return null;
            }
            switch (ms["tag"])
            {
                case "input":
                    var type = ms["type"];
                    switch (type)
                    {
                        case "radio":
                        case "checkbox":
                            if (!ms.ContainsKey("checked") && isForm)
                            {
                                return null;
                            }
                            return ms["value"] ?? "on";
                    }
                    return ms["value"];

                case "select":

                    var end5 = html.IndexOf("</select>"u8, false, endIndex);

                    var optionHtml = html.Slice(endIndex + 1, end5 - endIndex - 1);

                    var selectedIndex = optionHtml.IndexOf(" selected"u8, true);// StringComparison.CurrentCultureIgnoreCase);

                    if (selectedIndex == -1)
                    {
                        selectedIndex = optionHtml.IndexOf("option"u8);
                        if (selectedIndex == -1)
                        {
                            return String.Empty;
                        }
                    }
                    int optionEndIndex;
                    var ov = FromValue(selectedIndex, optionHtml, out optionEndIndex);
                    if (ov.ContainsKey("value"))
                    {
                        return ov["value"];
                    }
                    else
                    {
                        if (html[endIndex - 1] == '/')
                        {
                            return String.Empty;
                        }
                        var end9 = optionHtml.IndexOf((byte)'<', optionEndIndex);
                        return optionHtml.Slice(optionEndIndex + 1, end9 - optionEndIndex - 1).UTF8();

                    }

                case "textarea":
                    if (html[endIndex - 1] == '/')
                    {
                        return ms["value"] ?? String.Empty;
                    }
                    var end4 = html.IndexOf((byte)'<', endIndex);
                    if (end4 > 0)
                    {
                        return System.Web.HttpUtility.HtmlDecode(html.Slice(endIndex + 1, end4 - endIndex - 1).UTF8());
                    }
                    break;
            }
            return null;
        }
        public static string Expire(uint now, uint expireTime, string defaultStr)
        {
            var sExpireTime = defaultStr;// "未启用";
            if (expireTime > 0)
            {
                if (expireTime == Int32.MaxValue)
                {
                    sExpireTime = $"永不过期";
                }
                else if (expireTime > now)
                {
                    var t = new TimeSpan(0, 0, (int)(expireTime - now)).TotalDays;
                    if (t < 0)
                    {
                        sExpireTime = $"还剩{t:0.0}天";
                    }
                    else
                    {
                        sExpireTime = $"还剩{t:0}天";
                    }
                }
                else
                {
                    sExpireTime = "已过期";
                }
            }
            return sExpireTime;
        }
        public static void Shared(int size, Action<byte[]> action)
        {

            var bufer = System.Buffers.ArrayPool<byte>.Shared.Rent(size);
            try
            {
                action(bufer);
            }
            finally
            {
                System.Buffers.ArrayPool<byte>.Shared.Return(bufer);

            }
        }

        public static void SyncClient(byte type, params StringValue[] stringValues)
        {
            Utility.Shared(200, async buffer =>
            {
                try
                {
                    using (var tcpClient = new TcpClient())
                    {
                        var t = Utility.TimeSpan().StringValue();
                        t.Span.Reverse();

                        t.Span.CopyTo(buffer);
                        WebServlet.AppId.Span.CopyTo(buffer.AsSpan(4));
                        WebServlet.AppSecret.Span.CopyTo(buffer.AsSpan(20));
                        Utility.MD5(buffer.AsSpan(0, 36), buffer.AsSpan(7));
                        t.Span.CopyTo(buffer.AsSpan(3));

                        buffer[0] = 0x06;
                        buffer[1] = type;
                        buffer[2] = 20;
                        int index = 23;
                        foreach (var item in stringValues)
                        {
                            buffer[index] = (byte)item.Length;
                            item.Span.CopyTo(buffer.AsSpan(index + 1));
                            index += item.Length + 1;
                        }
                        buffer[index] = 0x00;
                        index++;
                        tcpClient.Connect("127.0.0.1", 443);
                        using (var stream = tcpClient.GetStream())
                        {
                            await stream.WriteAsync(buffer.AsMemory(0, index));
                        }
                    }
                }
                catch { }
            });

        }


        public static void Certificate(NetHttpResponse r)
        {
            if (r.StatusCode == System.Net.HttpStatusCode.OK)
            {
                r.ReadAsString(str =>
                {
                    var cert = JSON.Deserialize<WebMeta>(str);
                    if (cert.ContainsKey("privateKey"))
                    {
                        var domain = cert["domain"];
                        var privateKey = cert["privateKey"];
                        var publicKey = cert["publicKey"];

                        var x509 = X509Certificate2.CreateFromPem(publicKey, privateKey);

                        Certificater.Certificates[domain] = new Certificater(domain, x509);

                        HotCache.Put(new Entities.SiteCert
                        {
                            Domain = new StringValue(domain),
                            ExpirationTime = Utility.TimeSpan(x509.NotAfter),
                            CheckTime = Utility.TimeSpan(),
                            PrivateKey = new StringValue(privateKey),
                            PublicKey = new StringValue(publicKey),
                            IsApiumc = false
                        });
                    }
                });
            }
            else
            {
                r.ReadAsString(x => { });
            }
        }
        public static Web.WebMeta FromValue(Span<byte> html, bool isKey)
        {

            var webMeta = new System.Collections.Generic.Dictionary<String, List<String>>();
            var nKey = " name="u8;
            int index = html.IndexOf(nKey);
            while (index > 0)
            {
                var startIndex = index + nKey.Length;
                var start = html[startIndex];
                switch (start)
                {
                    case 34:
                    case 39:
                        // case (byte)'\'':
                        // case (byte)'"':
                        var endIndex = html.IndexOf(start, startIndex + 1);
                        if (endIndex > startIndex)
                        {
                            var name = html.Slice(startIndex + 1, endIndex - startIndex - 1).UTF8();
                            var value = FromValue(html, index, !isKey, out endIndex);
                            if (value != null)
                            {
                                List<String> vs;
                                if (webMeta.TryGetValue(name, out vs))
                                {
                                    vs.Add(value);
                                }
                                else
                                {
                                    vs = new List<string>
                                    {
                                        value
                                    };
                                    webMeta[name] = vs;
                                }

                                startIndex = endIndex;
                            }
                            else
                            {

                                startIndex = endIndex;
                            }
                        }
                        break;
                }
                index = html.IndexOf(nKey, false, startIndex);
            }
            var meta = new Web.WebMeta();
            var em = webMeta.GetEnumerator();
            while (em.MoveNext())
            {
                meta.Put(em.Current.Key, String.Join(",", em.Current.Value.ToArray()));
            }
            return meta;
        }
        public static NetHttpRequest Sign(NetHttpRequest http, String secret)
        {
            http.Add(new HeadValue("umc-app-version", APIProxy.Version));
            http.Add(new HeadValue("umc-proxy-sites", HotCache.Caches().First(r => r.Name == "Site").Count.ToString()));
            http.Add(new HeadValue("umc-proxy-session", HotCache.Caches().First(r => r.Name == "Session").Count.ToString()));
            http.Add(new HeadValue("umc-client-pfm", "sync"));
            http.Add(new HeadValue("umc-request-time", UMC.Data.Utility.TimeSpan().ToString()));
            if (String.IsNullOrEmpty(secret) == false)
            {
                http.Add("umc-request-sign", UMC.Data.Utility.Sign(http.Headers, "umc-"u8, secret));
            }

            return http;
        }
        public static int Random(StringValue val)
        {
            var fs = val.Span;
            if (fs.Length >= 4)
            {
                int v = BitConverter.ToInt32(fs);
                for (var i = 4; i < fs.Length; i++)
                {
                    v = v ^ fs[i];
                }
                return v;
            }
            else
            {
                var b = new byte[4];
                fs.CopyTo(b.AsSpan(4 - fs.Length));
                return BitConverter.ToInt32(b);
            }

        }
        public static int Random(string val)
        {
            if (String.IsNullOrEmpty(val) == false)
            {
                var fs = System.Text.Encoding.UTF8.GetBytes(val);
                if (fs.Length >= 4)
                {
                    int v = BitConverter.ToInt32(fs);
                    for (var i = 4; i < fs.Length; i++)
                    {
                        v = v ^ fs[i];
                    }
                    return v;
                }
                else
                {
                    var b = new byte[4];
                    Array.Copy(fs, 0, b, 4 - fs.Length, fs.Length);
                    return BitConverter.ToInt32(b);
                }
            }
            return 0;

        }
        public static string FirstReplace(string val, string s, string t)
        {
            var index = val.IndexOf(s);
            if (index == -1)
            {
                return val;
            }
            else
            {
                return $"{val.Substring(0, index)}{t}{val.Substring(index + s.Length)}";
            }
        }
    }

}
